package com.web_bloks.project.pages.client;

import java.util.HashMap;

import com.google.gwt.core.client.GWT;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalSplitPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RichTextArea;
import com.google.gwt.user.client.ui.SourcesTabEvents;
import com.google.gwt.user.client.ui.TabListener;
import com.google.gwt.user.client.ui.TabPanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.TextBoxBase;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.VerticalSplitPanel;
import com.google.gwt.user.client.ui.Widget;

import com.web_bloks.document.client.DocumentLoader;
import com.web_bloks.types.client.StateType;
import com.web_bloks.url.client.Helper;
import com.web_bloks.user.client.User;
import com.web_bloks.content.client.ViewPage;
import com.web_bloks.document.client.Document;
import com.web_bloks.document.client.DocumentLoaderListener;
import com.web_bloks.document.client.DocumentLoaderResponse;
import com.web_bloks.document.client.NamespaceException;
import com.web_bloks.document.client.Title;
import com.web_bloks.document.client.TitleException;
import com.web_bloks.http.client.HtmlPostCall;
import com.web_bloks.http.client.HttpCall;
import com.web_bloks.user.client.StatusMessageServer;
import com.web_bloks.user.client.UserManager;
import com.web_bloks.user.client.UserMessageServer;
import com.web_bloks.widget.client.RichTextToolbarWidget;
import com.web_bloks.widget.client.RecentListWidget;
import com.web_bloks.system.client.Logger;

/*
 * URL:   #Edit?title=$title$
 * 
 * Templates:
 *  - Retrieve templates in the document
 *  - Update list of templates
 *  
 * Page Editing:
 *  - Load page to edit from the 'raw' namespace of the 'domain'
 *  - Check access right 
 *    -- Update user message
 *  
 *  - Extract template list
 *  - Update recently visited pages (support frequency?)
 *  
 * Page Updating:
 *  - Upload new revision
 *  - Set appropriate status flag based on response from server
 *  - Update recently changed page list
 * 
 * Input Title
 *  - trap RETURN key event 
 * 
 * Lists:
 *  - create EDITABLE anchors (?) probably need to trap selectItem event & do navigation
 * 
 */
public class EditPage extends Composite 
	implements ViewPage, DocumentLoaderListener, RequestCallback {

	UserManager um   = GWT.create(UserManager.class);
	PageMessages MSG = GWT.create(PageMessages.class);
	
	VerticalPanel vpanel = null;
	HorizontalSplitPanel horizontalSplitPanel;
	VerticalPanel verticalPanelContent;
	
	RichTextToolbarWidget richTextToolbarWidget;
	RichTextArea richTextArea = new RichTextArea();
	TextArea textArea;
	
	Button buttonSave;
	Button buttonLoad;
	TextBox textBoxInputTitle;
	
	// User Feedback
	UserMessageServer   umsg = null;
	StatusMessageServer smsg = null;
	
	// Document
	DocumentLoader dl = GWT.create(DocumentLoader.class);
	String currentPageName = null;
	Title currentTitle = null;
	Document currentDoc = null;
	
	// Saving
	HttpCall poster = GWT.create(HtmlPostCall.class);
	
	// ListBox
	RecentListWidget listBoxRecentList;
	RecentListWidget recentListPages;
	
	// Edit type
	boolean isRichType = true;
	TabPanel tabPanel = new TabPanel();
	
	public EditPage() {
		super();
		
		Logger.logInfo(getClass()+": Constructor");
		
		vpanel = new VerticalPanel();
		
		initWidget(vpanel);
		setSize("100%", "100%");
		
		vpanel.setSpacing(5);
		vpanel.setSize("100%", "100%");

		horizontalSplitPanel = new HorizontalSplitPanel();
		vpanel.add(horizontalSplitPanel);
		vpanel.setCellVerticalAlignment(horizontalSplitPanel, HasVerticalAlignment.ALIGN_TOP);
		vpanel.setCellHorizontalAlignment(horizontalSplitPanel, HasHorizontalAlignment.ALIGN_LEFT);
		horizontalSplitPanel.setSize("100%", "100%");
		vpanel.setCellHeight(horizontalSplitPanel, "100%");
		vpanel.setCellWidth(horizontalSplitPanel, "100%");
		horizontalSplitPanel.setSplitPosition("15%");

		verticalPanelContent = new VerticalPanel();
		horizontalSplitPanel.setRightWidget(verticalPanelContent);
		verticalPanelContent.setSpacing(10);
		verticalPanelContent.setSize("100%", "100%");

		richTextToolbarWidget = new RichTextToolbarWidget(richTextArea);
		final VerticalPanel verticalPanelRichText = new VerticalPanel();
		tabPanel.add(verticalPanelRichText, MSG.richTextArea() );
		verticalPanelRichText.setSize("98%", "100%");
		verticalPanelRichText.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
		verticalPanelRichText.setSpacing(5);
		verticalPanelRichText.add(richTextToolbarWidget);
		richTextToolbarWidget.setHeight("40");
		verticalPanelRichText.setCellVerticalAlignment(richTextToolbarWidget, HasVerticalAlignment.ALIGN_TOP);
		verticalPanelRichText.setCellHorizontalAlignment(richTextToolbarWidget, HasHorizontalAlignment.ALIGN_CENTER);

		//RichTextArea		
		verticalPanelRichText.add(richTextArea);
		richTextArea.setSize("100%", "400");
		verticalPanelRichText.setCellWidth(richTextArea, "100%");
		verticalPanelRichText.setCellHeight(richTextArea, "100%");
		richTextArea.setText("");
		verticalPanelRichText.setCellVerticalAlignment(richTextArea, HasVerticalAlignment.ALIGN_TOP);
		verticalPanelRichText.setCellHorizontalAlignment(richTextArea, HasHorizontalAlignment.ALIGN_CENTER);
		
		richTextArea.setTabIndex(3);
		

		verticalPanelContent.add(tabPanel);
		verticalPanelContent.setCellHeight(tabPanel, "100%");
		verticalPanelContent.setCellWidth(tabPanel, "100%");
		verticalPanelContent.setCellVerticalAlignment(tabPanel, HasVerticalAlignment.ALIGN_TOP);
		verticalPanelContent.setCellHorizontalAlignment(tabPanel, HasHorizontalAlignment.ALIGN_CENTER);
		tabPanel.setSize("100%", "100%");

		textArea = new TextArea();
		tabPanel.add(textArea, MSG.textArea() );
		textArea.setSize("100%", "100%");
		textArea.setTextAlignment(TextBoxBase.ALIGN_LEFT);
		textArea.setTabIndex(3);
		textArea.setCharacterWidth(120);
		textArea.setVisibleLines(20);
		
		//rich-text default
		tabPanel.selectTab(0);


		// SAVE
		buttonSave = new Button();
		verticalPanelContent.add(buttonSave);
		buttonSave.setSize("100", "25");
		verticalPanelContent.setCellWidth(buttonSave, "100");
		verticalPanelContent.setCellHeight(buttonSave, "25");
		verticalPanelContent.setCellVerticalAlignment(buttonSave, HasVerticalAlignment.ALIGN_TOP);
		verticalPanelContent.setCellHorizontalAlignment(buttonSave, HasHorizontalAlignment.ALIGN_CENTER);
		buttonSave.setAccessKey('S');
		buttonSave.setTabIndex(4);
		buttonSave.setText(MSG.action_save());
		buttonSave.setTitle(MSG.action_save_title());		

		//Sidebar
		final VerticalPanel verticalPanelSidebar = new VerticalPanel();
		horizontalSplitPanel.setLeftWidget(verticalPanelSidebar);
		verticalPanelSidebar.setSize("100%", "100%");

		final VerticalPanel verticalPanel = new VerticalPanel();
		verticalPanelSidebar.add(verticalPanel);
		verticalPanel.setSize("100%", "100%");

		textBoxInputTitle = new TextBox();
		verticalPanel.add(textBoxInputTitle);
		textBoxInputTitle.setTabIndex(1);
		textBoxInputTitle.setWidth("100%");
		verticalPanel.setCellWidth(textBoxInputTitle, "100%");
		textBoxInputTitle.setTitle( MSG.label_input_title() );

		// LOAD
		buttonLoad = new Button();
		verticalPanel.add(buttonLoad);
		verticalPanel.setCellVerticalAlignment(buttonLoad, HasVerticalAlignment.ALIGN_TOP);
		verticalPanel.setCellHorizontalAlignment(buttonLoad, HasHorizontalAlignment.ALIGN_CENTER);
		buttonLoad.setTabIndex(2);
		buttonLoad.setText(MSG.action_load());
		buttonLoad.setTitle(MSG.action_load_title());

		final HTML html = new HTML("<hr>");
		verticalPanel.add(html);
		verticalPanel.setCellVerticalAlignment(html, HasVerticalAlignment.ALIGN_TOP);

		final Label labelRecentList = new Label( MSG.label_recent_list());
		verticalPanel.add(labelRecentList);
		verticalPanel.setCellVerticalAlignment(labelRecentList, HasVerticalAlignment.ALIGN_MIDDLE);
		verticalPanel.setCellHorizontalAlignment(labelRecentList, HasHorizontalAlignment.ALIGN_CENTER);
		verticalPanel.setCellWidth(labelRecentList, "100%");


		recentListPages = new RecentListWidget( "pages" );
		recentListPages.setSize("100%", "100%");

		listBoxRecentList = new RecentListWidget( "rawpages" );
		listBoxRecentList.setSize("100%", "100%");

		final VerticalSplitPanel verticalSplitPanel = new VerticalSplitPanel();
		verticalPanel.add(verticalSplitPanel);
		verticalSplitPanel.setSize("100%", "100%");
		verticalPanel.setCellVerticalAlignment(verticalSplitPanel, HasVerticalAlignment.ALIGN_TOP);
		verticalPanel.setCellHorizontalAlignment(verticalSplitPanel, HasHorizontalAlignment.ALIGN_CENTER);
		verticalPanel.setCellHeight(verticalSplitPanel, "100%");
		verticalPanel.setCellWidth(verticalSplitPanel, "100%");
		verticalSplitPanel.setSplitPosition("50%");

		verticalSplitPanel.add(listBoxRecentList);
		verticalSplitPanel.add(recentListPages);
		
		//document loader
		dl.addListener(this);
		
		//document saver
		poster.setRequestCallback( this );
		poster.setHeader("Content-Type", "text/html");
		
		
		setupUIListeners();
		setupRecentListOnChange();
		setupTemplateListOnChange();
	}

	/*****************************************
	 * ViewPage INTERFACE 
	 *****************************************/
	public Widget getViewWidget() {
		return this;
	}
	public String getViewName() {
		return "Edit";
	}
	/*
	 * @see com.web_bloks.content.client.ViewPage#getPageName()
	 */
	public String getPageName() {
		return currentTitle.toString();
	}
	/**
	 * @see ViewPage
	 */
	public void setViewParams(String key, String value) {
	}
	/**
	 * @see ViewPage
	 */
	public void doView() {	
	}

	/**
	 * query:  title=$title$
	 * 
	 */
	public void setRequestedPageParams(String requestedPage, String query) {
		
		// extract the fields
		HashMap<String,String> params = Helper.extractAndMapQuery(query);
		if ( null == params ) {
			umsg.sendMessage( MSG.invalid_page() );
			Logger.logDebug( getClass()+"setRequestedPageParams: missing params" );
			currentTitle = null;
			return;
		}
		// make sure the mandatory 'title' field is around
		if ( !params.containsKey("title") ) {
			umsg.sendMessage( MSG.invalid_page() );
			Logger.logDebug( getClass()+"setRequestedPageParams: missing 'title' param" );
			currentTitle = null;
			return;
		}
		Exception exception = null;
		// construct the Title instance
		Title title = null;
		Title rawTitle = null;
		try {
			title=Title.newFromText( params.get("title") );
			// we also need the title from the 'raw' namespace
			rawTitle = title.getRawTitle();			
		} catch (TitleException e) {
			exception = e;
		} catch (NamespaceException e) {
			exception = e;
		}
		// a bad title it seems
		if (null!=exception) {
			smsg.setStatus( StateType.ERROR );
			currentTitle = null;
			return;
		}
		currentTitle    = title;
		
		try {
			dl.load( rawTitle.toString() );
		} catch (RequestException e) {
			smsg.setStatus( StateType.ERROR );
		}
		
		listBoxRecentList.addEntry(rawTitle.toString(), "" );
		recentListPages.addEntry( title.toString(), "" );
	}//
	
	public void setUserMessageServer(UserMessageServer umsg) {
		this.umsg = umsg;
	}
	public void setStatusMessageServer(StatusMessageServer smsg) {
		this.smsg = smsg;
	}

	public void doUpdate() {
		
	}
	/*************************************************************
	 * DocumentLoaderListener
	 ************************************************************/
	public void onDocumentLoaderResponse(	Document response, 
											DocumentLoaderResponse LoaderResponseCode) {

		// update status
		if (LoaderResponseCode.compareCode(DocumentLoaderResponse.OK)) {
			smsg.setStatus( StateType.OK );
			currentDoc = response;
			textArea.setText( currentDoc.getPageContent() );
			richTextArea.setHTML( currentDoc.getPageContent() );
		} else {
			smsg.setStatus( StateType.ERROR );
			currentDoc = null;
			richTextArea.setText( "" );
			textArea.setText( "" );
		}
		
		updateUI();
		
		processTemplate();
		
	}//

	protected void processTemplate() {
		
		if (null==currentDoc) return;
		
		String templateStringList = extractTemplateList( currentDoc.getPageContent() );
		
		Logger.logInfo( getClass()+".processTemplate: " + templateStringList );
		
	}//
	/**
	 * e.g. <!--<templates>JSON-Object</templates>-->
	 * 
	 * 
	 * @param content
	 * @return
	 */
	protected String extractTemplateList( String content ) {
		
		final String START = "<templates>";
		final String END   = "</templates>";
		
		if (null==content) return null;
		
		int iStart = content.indexOf( START  );
		int iEnd   = content.indexOf( END );
		
		if ( -1==iStart || -1==iEnd ) return null;
		
		return content.substring(iStart+START.length(), iEnd );
	}//
	
	/*************************************************************
	 * PROTECTED
	 ************************************************************/
	/**
	 * Only makes sense to enable the 'save' button when the
	 * user can actually save the document.
	 */
	protected void updateUI() {
		User currentUser = um.getCurrentUser();
		
		boolean admin = currentUser.isMemberOf("admin"); 
		
		buttonSave.setEnabled( admin );		
		
		buttonLoad.setEnabled( true );
	}

	protected void setupUIListeners() {
		
		/**
		 * 
		 */
		buttonLoad.addClickListener( new ClickListener() {

			public void onClick(Widget sender) {
				buttonLoad.setEnabled( false );
				smsg.setStatus( StateType.PENDING );
				loadSpecifiedDocument();
			}
			
		});

		/**
		 * Action to save the current document.
		 */
		buttonSave.addClickListener( new ClickListener() {

			public void onClick(Widget sender) {
				saveCurrentDocument();
			}
			
		});

		/**
		 * Action to save the current document.
		 */
		tabPanel.addTabListener( new TabListener() {

			public boolean onBeforeTabSelected(SourcesTabEvents sender, int tabIndex) {
				return true;
			}

			public void onTabSelected(SourcesTabEvents sender, int tabIndex) {
				updateEditType( tabIndex==0 );
			}
			
		});

	}//
	
	/**
	 * 
	 * @param richType
	 */
	protected void updateEditType(boolean richType) {

		isRichType = richType;
		
		if (richType) {
			richTextToolbarWidget.setVisible( true );
			richTextArea.setHTML( textArea.getText() );
		} else {
			richTextToolbarWidget.setVisible( false );			
			textArea.setText( richTextArea.getHTML() );
		}
		
	}
	
	/**
	 * Performs document saving to server
	 */
	protected void saveCurrentDocument() {
		
		buttonSave.setEnabled( false );
		smsg.setStatus( StateType.PENDING );
		
		String content = (isRichType ? richTextArea.getHTML() : textArea.getText() );
		
		try {
			poster.execute("/page/"+currentTitle.toString()+"/", content );
		} catch (RequestException e) {
			Logger.logError( getClass()+".saveCurrentDocument: exception raised, msg="+e.getMessage() );
			
			smsg.setStatus( StateType.ERROR );
			umsg.sendMessage( MSG.post_error() );

			updateUI();			
		}

	}
	/**
	 * Loads the document specified in the textbox
	 */
	protected void loadSpecifiedDocument() {
		
		String pageName = textBoxInputTitle.getText();

		Exception e = null;
		try {
			final Title title = Title.newFromText( pageName );
			
			DeferredCommand.addCommand( new Command() {
				public void execute() {
					History.newItem( "Edit?title=" + title.toString() );
				}
			});
			
		} catch (TitleException e1) {
			e = e1;
		} catch (NamespaceException e1) {
			e = e1;
		}
		
		if (null != e) {
			Logger.logInfo( getClass()+".loadSpecifiedDocument: invalid title");
			umsg.sendMessage( MSG.invalid_page() );
			updateUI();
			return;
		}
		
		
	}//

	/*****************************************
	 * RequestCallback -- Saving related
	 *****************************************/
	
	public void onError(Request request, Throwable exception) {
		smsg.setStatus( StateType.ERROR );
		umsg.sendMessage( MSG.post_error() );
		updateUI();
	}
	public void onResponseReceived(Request request, Response response) {
		smsg.setStatus( StateType.OK );
		umsg.sendMessage( MSG.post_ok() );
		updateUI();
	}

	/*****************************************
	 * RecentList
	 *****************************************/
	protected void setupRecentListOnChange() {
		
		listBoxRecentList.addChangeListener( new ChangeListener(){

			public void onChange(Widget sender) {
				
				int selectedIndex = listBoxRecentList.getSelectedIndex();
				if (-1==selectedIndex) return;
				
				final String item = listBoxRecentList.getItemText( selectedIndex );

				DeferredCommand.addCommand( new Command() {
					public void execute() {
						History.newItem( "Edit?title=" + item );
					}
				});				
			}
			
		});		
		
		recentListPages.addChangeListener( new ChangeListener(){

			public void onChange(Widget sender) {
				
				int selectedIndex = recentListPages.getSelectedIndex();
				if (-1==selectedIndex) return;
				
				final String item = recentListPages.getItemText( selectedIndex );

				DeferredCommand.addCommand( new Command() {
					public void execute() {
						History.newItem( item );
					}
				});				
			}
			
		});		
		
	}//

	/*****************************************
	 * Template List
	 *****************************************/
	protected void setupTemplateListOnChange() {
		
	}//
	
	
}//END
